package org.liziyi.huffman;


import org.liziyi.huffman.algorithm.HuffmanAlgorithm;
import org.liziyi.huffman.common.FileContext;
import org.liziyi.huffman.config.Config;
import org.liziyi.huffman.gui.*;
import org.liziyi.huffman.utils.FileUtils;

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.DefaultTableModel;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;

/**
 * @author liziyi
 */
public class HuffmanApplication extends JFrame {
    // 界面相关
    private FileChooseGUI fileChooseGUI;
    private FileDisplayGUI fileDisplayGUI;
    private TreeDisplayGUI treeDisplayGUI;
    private CharMapComponent charMapComponent;
    // 核心算法
    private HuffmanAlgorithm huffmanAlgorithm;

    public HuffmanApplication() {
        super("Huffman");
        init();
    }

    /**
     * 显示GUI
     */
    public void showGUI() {
        this.setVisible(true);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setResizable(false);
        setPage(fileChooseGUI.getBaseJPanel(), Config.fileChooseGUIWidth, Config.fileChooseGUIHeight);
    }

    /**
     * 初始化，包括子gui构建和注册回调
     */
    private void init() {
        fileChooseGUI = new FileChooseGUI();
        fileDisplayGUI = new FileDisplayGUI();
        treeDisplayGUI = new TreeDisplayGUI();
        charMapComponent = new CharMapComponent();
        huffmanAlgorithm = new HuffmanAlgorithm();
        // 注册回调
        registFileChooseGUICallback();
        registFileDisplayGUICallback();
        registTreeDisplayGUICallback();
    }

    private void registFileChooseGUICallback() {
        // 点击 choose file
        fileChooseGUI.getChooseFileButton().addActionListener(actionEvent -> {
            // 显示文件选择器
            JFileChooser fileChooser = new JFileChooser();
            fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            fileChooser.setAcceptAllFileFilterUsed(false);
            FileNameExtensionFilter filter = new FileNameExtensionFilter("文本文件", "txt");
            fileChooser.addChoosableFileFilter(filter);
            int result = fileChooser.showOpenDialog(this);
            if (result != JFileChooser.APPROVE_OPTION) return;
            // 获取选择的文件的绝对路径
            String path = fileChooser.getSelectedFile().getAbsolutePath();
            if (path == null || path.length() == 0) return;
            // 读取文件内容 (初始化文件上下文)
            FileContext.resetContext(FileUtils.readFile(path), null, null);
            // 显示文件内容
            fileDisplayGUI.getFileJTestArea().setText(FileContext.fileContent);
            // 切换到文件显示页面
            setPage(fileDisplayGUI.getBaseJPanel(), Config.fileDisplayGUIWidth, Config.fileDisplayGUIHeight);
            // 初始显示文本组件
            table2text();
        });
    }

    private void registFileDisplayGUICallback() {
        // 从文件显示页返回文件选择页
        fileDisplayGUI.getBack2ChooseFileJButton().addActionListener(actionEvent -> {
            setPage(fileChooseGUI.getBaseJPanel(), Config.fileChooseGUIWidth, Config.fileChooseGUIHeight);
        });

        // 点击 encode
        fileDisplayGUI.getEncodeJButton().addActionListener(actionEvent -> {
            // 设置编码后的内容，如果是首次需要调用算法
            fileDisplayGUI.getFileJTestArea().setText(
                    Optional.ofNullable(FileContext.fileContentAfterEncode)
                            .orElse(FileContext.fileContentAfterEncode=huffmanAlgorithm.encode(FileContext.fileContent)));
            // 显示文本组件
            table2text();
        });

        // 点击 decode
        fileDisplayGUI.getDecodeJButton().addActionListener(actionEvent -> {
            if (FileContext.fileContentAfterEncode == null) {
                JOptionPane.showMessageDialog(this, "Please Encode First!");
                return;
            }
            // 设置解码后的内容，如果是首次需要调用算法
            fileDisplayGUI.getFileJTestArea().setText(
                    Optional.ofNullable(FileContext.fileContentAfterDecode)
                            .orElse(FileContext.fileContentAfterDecode=huffmanAlgorithm.decode(FileContext.fileContentAfterEncode)));
            // 显示文本组件
            table2text();
        });

        // 点击 show mapping
        fileDisplayGUI.getShowCharMapJButton().addActionListener(actionEvent -> {
            if (FileContext.fileContentAfterEncode == null) {
                JOptionPane.showMessageDialog(this, "Please Encode First!");
                return;
            }
            String[] header = new String[]{"Original Char", "Char Num", "Huffman code"};
            charMapComponent.getCharMapJTable().setModel(new DefaultTableModel(getStatisticData(), header){
                @Override
                public boolean isCellEditable(int row, int column) {
                    return false;
                }
            });
            // 显示表格组件，用以展示映射
            text2table();
        });

        // 点击show tree
        fileDisplayGUI.getShowTreeJButton().addActionListener(actionEvent -> {
            if (FileContext.fileContentAfterEncode == null) {
                JOptionPane.showMessageDialog(this, "Please Encode First!");
                return;
            }
            // 注入要绘制的huffman树信息
            ((PaintHuffmanTreeJPanel) treeDisplayGUI.getPaintBoardJPanel())
                    .injectHuffmanTreeInfo(huffmanAlgorithm.getHuffmanTree(), huffmanAlgorithm.getHuffmanTreeLayer(),1.0f);
            setPage(treeDisplayGUI.getBaseJPanel(), Config.treeDisplayGUIWidth, Config.treeDisplayGUIHeight);
        });
    }

    private void registTreeDisplayGUICallback() {
        treeDisplayGUI.getBack2FileDisplayJButton().addActionListener(actionEvent -> {
            setPage(fileDisplayGUI.getBaseJPanel(), Config.fileDisplayGUIWidth, Config.fileDisplayGUIHeight);
        });
        treeDisplayGUI.getBiggerJButton().addActionListener(actionEvent -> {
            // 更改尺寸因子
            ((PaintHuffmanTreeJPanel) treeDisplayGUI.getPaintBoardJPanel())
                    .changeSizeScale(Config.treeScaleBigger);
            treeDisplayGUI.getPaintBoardJPanel().repaint();

        });
        treeDisplayGUI.getSmallerJButton().addActionListener(actionEvent ->{
            // 更改尺寸因子
            ((PaintHuffmanTreeJPanel) treeDisplayGUI.getPaintBoardJPanel())
                    .changeSizeScale(Config.treeScaleSmaller);
            treeDisplayGUI.getPaintBoardJPanel().repaint();

        });
    }

    private String[][] getStatisticData() {
        // 获取字符统计
        Map<Character, Integer> charStatistic = huffmanAlgorithm.getCharStatistic();
        // 获取映射数据
        Map<Character, String> charMap = huffmanAlgorithm.getCharMap();
        String[][] data = new String[charMap.size()][3];
        int index = 0;
        for (Map.Entry<Character, String> entry : charMap.entrySet()) {
            if(entry.getKey()==' ') data[index][0]="space";
            else if(entry.getKey()=='\n') data[index][0]="\\n";
            else if(entry.getKey()=='\r') data[index][0]="\\r";
            else data[index][0] = entry.getKey() + "";
            data[index][1] = String.valueOf(charStatistic.get(entry.getKey()));
            data[index++][2] = entry.getValue();
        }
        // 按字符出现频次倒序
        Arrays.sort(data, (a, b) -> Integer.parseInt(b[1]) - Integer.parseInt(a[1]));
        return data;
    }

    private void text2table() {
        fileDisplayGUI.getJscrollPane().setViewportView(charMapComponent.getBaseJPanel());
    }

    private void table2text() {
        fileDisplayGUI.getJscrollPane().setViewportView(fileDisplayGUI.getFileJTestArea());
    }

    /**
     * 用于切换界面
     *
     * @param target
     * @param width
     * @param height
     */
    private void setPage(JPanel target, int width, int height) {
        this.setContentPane(target);
        this.setSize(width, height);
        this.setLocationRelativeTo(null);
    }
}
